#include <typeinfo>
#include <cstring>

#include "base/Entity.h"
#include "base/Packet.h"
#include "base/Request.h"
#include "base/Response.h"
#include "util/Logger.h"
#include "util/SimSupers.h"

NEW_DELETE_OPERATORS_CPP(Entity,1);

Entity::Entity (const char* name, ClockOrder clockOrder) {    
    this->clockOrder = clockOrder;
    ASSERT(CLOCK_FIRST <= clockOrder && clockOrder <= CLOCK_LAST && clockOrder != CLOCK_SIZE);
    this->name = (char*)CALLOC(strlen(name) + 1, sizeof(char*));
    strcpy(this->name, name);
    this->active = false;
}

Entity::~Entity () {
    FREE(name);
}

ClockOrder Entity::getClockOrder () {
    return clockOrder;
}

void Entity::activate () {
    ASSERT(!active);
    active = true;
}

void Entity::deactivate () {
    ASSERT(active);
    active = false;
}

Result Entity::handle (Packet* r) {
    const std::type_info *orig_type = &typeid(*r);
    const std::type_info *type = orig_type;

    while (type != &typeid(NoSuper) && type != NULL) {
        if (dispatchTable.find(type) != dispatchTable.end()) {
            PacketHandlerPtr f = dispatchTable[type];
            ASSERT(f != NULL);
            if (type != orig_type) {
                registerPacketHandler(*orig_type, f);
            }
            return (this->*f)(r);
        } 
        Log->d("Couldn't find handler in entity %s for packet of type %s, looking for one for ", 
               getName(), type->name());
        type = simSupers()[type];
        Log->d("%s...\n", type->name()); 
    }
    
    Log->e("Couldn't find handler in entity %s for packet of type %s (%s).\n", 
           getName(), orig_type->name(), r->toString());
    return failure; 
}

void Entity::registerPacketHandler(const std::type_info& type,
                                   PacketHandlerPtr f)
{ 
    dispatchTable[&type] = f;
}

void Entity::ignorePacketType(const std::type_info& type) { 
    registerPacketHandler(type, (PacketHandlerPtr)&Entity::ignorePacket);
}

void Entity::sinkPacketType (const std::type_info& type) {
    if (!is_a(typeid(Request), type)) {
        Log->e("Can't sink a non-Request type.\n");
    }
    registerPacketHandler(type, (PacketHandlerPtr)&Entity::sinkPacket);
}

Result Entity::sinkFailed (Request* req, Response* resp, Result res) {
    Log->e("I tried to sink packet %s by responding with %s, but that failed with Result %d.\n",
           req->toString(), resp->toString(), res);
    return failure;
}

Result Entity::sinkPacket (Packet* p) {
    Request* req = CAST_OR_DIE(Request*, p);
    Response* resp = NEW Response(req, 1);
    Result res = routePacket(resp);
    if (res != success) {
        return sinkFailed(req, resp, res);
    }
    return success;
}

Result Entity::ignorePacket (Packet* p) {
    return success;
}

void Entity::addRoute (Entity* src, Entity* dest) {
    routes[src] = dest;
}

void Entity::deleteRoute (Entity* src) {
    routes.erase(src);
}

// is e on the route from this to dest?
//
bool Entity::onRoute (Entity* dest, Entity* e) {
    if (dest == e) {
        return true;
    }

    if (routes.find(dest) == routes.end()) {
        // you cant get there from here...
        return false;
    }

    Entity* p = this;
    while (p->routes.find(dest) != p->routes.end()) {
        p = p->routes[dest];
        if (p == e) {
            return true;
        }

        // end of the road...
        if (p == dest) {
            break;
        }
    }
    return false;
}

Result Entity::routePacket (Packet* pk) {
    if (!pk) {
        Log->e("%s::routePacket(): packet is NULL\n", getName());
    }

    Entity* dest = pk->getDestination();
    if (!dest) {
        Log->e("%s::routePacket(): packet does not have a destination!\n", getName());
    }

    Entity* nextHop = routes[dest];
    if (nextHop == NULL) {
        Log->e("%s::routePacket(): [%s] unknown destination: %s\n",
               getName(),
               pk->toString(),
               dest->getName());
    }
    return nextHop->handle(pk);
}
